package com.example.zxapigateway.filters;

import com.zx.zxapiclientsdk.utils.SignUtils;
import com.zx.zxapicommon.model.entity.Auth;
import com.zx.zxapicommon.model.entity.InterfaceInfo;
import com.zx.zxapicommon.service.InnerAuthService;
import com.zx.zxapicommon.service.InnerInterfaceInfoService;
import com.zx.zxapicommon.service.InnerUserInterfaceInfoService;
import com.zx.zxapicommon.service.InnerUserService;
import lombok.extern.slf4j.Slf4j;
import org.apache.dubbo.config.annotation.DubboReference;
import org.jetbrains.annotations.NotNull;
import org.reactivestreams.Publisher;
import org.springframework.cloud.gateway.filter.GatewayFilter;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.support.ServerWebExchangeUtils;
import org.springframework.core.Ordered;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.core.io.buffer.DataBufferFactory;
import org.springframework.core.io.buffer.DataBufferUtils;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.http.server.reactive.ServerHttpResponseDecorator;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

/**
 * @author 张旭
 * @version 1.0
 */
@Component
@Slf4j
public class InterfaceFilter implements GatewayFilter, Ordered {


    @DubboReference
    private InnerAuthService innerAuthService;
    @DubboReference
    InnerUserService innerUserService;
    @DubboReference
    InnerUserInterfaceInfoService innerUserInterfaceInfoService;
    @DubboReference
    InnerInterfaceInfoService innerInterfaceInfoService;

    private static final String PATH_PREFIX = "http://localhost:8003";
    //IP访问白名单
    public static final List<String> IP_WHITE_List = Arrays.asList("0:0:0:0:0:0:0:1");

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {


        /**
         * 1.记录请求日志——由CustomGlobalFilter完成
         * 由于order的设置，请求会先到CustomGlobalFilter
         * 可以获取请求路径
         */
        ServerHttpRequest request = exchange.getRequest();


        /**
         * 2.黑白名单限制
         */
        ServerHttpResponse response = exchange.getResponse();
        /*if (!IP_WHITE_List.contains(address)) {
            return handleNoAuth(response);
        }*/

        /**
         * 3.验证用户的调用信息是否正确
         */
        HttpHeaders headers = request.getHeaders();
        String userId = headers.getFirst("userId");
        String appId = headers.getFirst("appId");
        String accessKey = headers.getFirst("accessKey");

        String nonce = headers.getFirst("nonce");
        String timestamp = headers.getFirst("timestamp");

        String sign = headers.getFirst("sign");
        String body = headers.getFirst("body");

        Auth auth = null;
        //远程调用用户权限信息
        auth = innerAuthService.getAuth(appId, userId);

        //校验权限信息
        if (null == auth) {
            return handleNoAuth(response);
        }
        //校验ak
        if (accessKey.isEmpty()) {
            return handleNoAuth(response);
        }
        if (!auth.getAccessKey().equals(accessKey)) {
            return handleNoAuth(response);
        }
        //校验随机数
        //todo 改为redis实现
        if (nonce.length() > 4) {
            return handleNoAuth(response);
        }
        //校验时间戳
        final long FIVE_MINUTES = 5 * 60L;
        long currentTime = System.currentTimeMillis() / 1000;
        if ((currentTime - Long.parseLong(timestamp)) > FIVE_MINUTES) {
            return handleNoAuth(response);
        }

        //校验sk，根据当前的用户的sk跟传来的sk做对比
        String secretKey = null;
        secretKey = auth.getSecretKey();
        String serverSign = SignUtils.getSign(body, secretKey);
        if (sign == null || !sign.equals(serverSign)) {
            return handleNoAuth(response);
        }

        //判断当前请求接口是否存在
        String url = headers.getFirst("url");
        String interfaceId = headers.getFirst("interfaceId");
        InterfaceInfo interfaceInfo = null;
        try {
            interfaceInfo = innerInterfaceInfoService.getInterfaceInfo(interfaceId, url);
        } catch (Exception e) {
            log.error("getInterface error:{}", e.getMessage());
        }

        if (null == interfaceInfo) {
            log.error("请求的接口不存在，接口id:{}、url:{}", interfaceId, url);
            return handleNoAuth(response);
        }
        //当前用户是否还有调用次数
        boolean hasCount = innerUserInterfaceInfoService.checkUserInvokeCount(auth.getUserid(), interfaceId);
        if (hasCount == false) {
            log.error("接口没有调用次数 用户id，接口id:{}，{}", auth.getUserid(), interfaceId);
            return handleNoAuth(response);
        }

        //请求转发，调用模拟接口
       // Mono<Void> filter = chain.filter(exchange);

        //记录响应日志
        //调用成功，接口调用次数-1
        //调用失败，返回规范错误码
        return handleResponse(exchange, chain, interfaceInfo.getId(), auth.getUserid());
    }

    private Mono<Void> handleNoAuth(ServerHttpResponse response) {
        response.setStatusCode(HttpStatus.FORBIDDEN);
        return response.setComplete();
    }

    @Override
    public int getOrder() {
        return -2;
    }

    public Mono<Void> handleResponse(ServerWebExchange exchange, GatewayFilterChain chain, Long interfaceInfoId, Long userId) {
        try {
            //从交换寄拿响应对象
            ServerHttpResponse originalResponse = exchange.getResponse();
            //缓冲区工厂，拿到缓存数据
            DataBufferFactory bufferFactory = originalResponse.bufferFactory();
            //拿到响应码
            HttpStatus statusCode = originalResponse.getStatusCode();

            if (statusCode == HttpStatus.OK) {
                //装饰，增强能力
                ServerHttpResponseDecorator decoratedResponse = new ServerHttpResponseDecorator(originalResponse) {
                    //等调用完转发的接口后才会执行
                    @NotNull
                    @Override
                    public Mono<Void> writeWith(Publisher<? extends DataBuffer> body) {
                        log.info("body instanceof Flux: {}", (body instanceof Flux));
                        //对象是响应式的
                        if (body instanceof Flux) {
                            //我们拿到真正的body
                            Flux<? extends DataBuffer> fluxBody = Flux.from(body);
                            //往返回值里面写数据
                            //拼接字符串
                            return super.writeWith(fluxBody.map(dataBuffer -> {
                                //  调用成功，次数+1
                                try {
                                    innerUserInterfaceInfoService.invokeCount(interfaceInfoId, userId);
                                } catch (Exception e) {
                                    log.error("invokeCount error:{}", e.getMessage());
                                }
                                byte[] content = new byte[dataBuffer.readableByteCount()];
                                dataBuffer.read(content);
                                DataBufferUtils.release(dataBuffer);//释放掉内存
                                // 构建日志
                                StringBuilder sb2 = new StringBuilder(200);
                                List<Object> rspArgs = new ArrayList<>();
                                rspArgs.add(originalResponse.getStatusCode());
                                String data = new String(content, StandardCharsets.UTF_8);//data 响应结果数据
                                sb2.append(data);
                                // 打印日志
                                log.info("响应结果：{}", data);
                                return bufferFactory.wrap(content);
                            }));
                        } else {
                            // 9. 调用失败，返回规范错误码
                            log.error("<--- {} 响应code异常", getStatusCode());
                        }
                        return super.writeWith(body);
                    }
                };
                // 调用模拟接口,设置 response 对象为装饰过的
                return chain.filter(exchange.mutate().response(decoratedResponse).build());
            }
            //降级处理返回数据
            return chain.filter(exchange);
        } catch (Exception e) {
            log.error("网关响应异常：" + e);
            return chain.filter(exchange);
        }
    }


}
